package org.broadway.monitor;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadway.exceptions.EvaluationException;
import org.broadway.interfaces.Evaluator;
import org.broadway.scripting.BsfScriptEngine;
import org.broadway.scripting.ScriptEngine;

/***
 * This class implements a string expression evaluator.
 * This evaluator can be used to evaluate expressions in a given
 * scripting language.
 * The evaluator evaluates arbitrary conditions of scanned
 * objects stored in the collector.
 * See {@link #org.broadway.interfaces.Scanner},
 *     {@link #org.broadway.interfaces.ResourceCollector}
 * @author vmatters
 *
 */
public class StringExpressionEvaluator implements Evaluator<Boolean,String> {
	private static Log log = LogFactory.getLog(StringExpressionEvaluator.class);

	private ScriptEngine engine;

	/**
	 * Constructor
	 *
	 */
	public StringExpressionEvaluator() {
	}

	/**
	 * Constructor with a default ScriptEngine.
	 * See {@link #org.broadway.org.scripting.ScriptEngine}
	 * @param eng
	 */
	public StringExpressionEvaluator(ScriptEngine eng) {
		this();
		setScriptEngine(eng);
	}

	/**
	 * This is the Evaluator's implemented method.  It uses
	 * string literal expression to evaluates arbitrary based on values passed
	 * as parameters (see {@link #putParameter(String, Object)}.
	 */
	public Boolean evaluate(String exp) throws EvaluationException {
		log.debug("Evaluating expression " + exp);
		validate(exp);
		Boolean result = (Boolean) this.getScriptEngine().eval(exp);
		if(result != null && result instanceof Boolean){
			return (Boolean)result;
		}else
			throw new EvaluationException("Evaluation Error - The result of the StringLiteralEvaluator must resolve to a boolean value.");
	}

	/**
	 * Setter for the script engine which will be used to evaluate string expression.
	 * @param eng ScriptEngine
	 */
	public void setScriptEngine(ScriptEngine eng) {
		this.engine = eng;
	}

	/**
	 * Returns the evaluator's script engine (if any).
	 * @return
	 */
	public ScriptEngine getScriptEngine() {
		if(engine == null)
			engine = new BsfScriptEngine();
		return engine;
	}

	/**
	 * Adds paramter values that will be evaluated by the evaluator's expression.
	 * Added parameters can be accessed using scripting expressions
	 * to bind to the added value.  For instance, if a object A with
	 * getters A.getLabel() and A.getLetter is added using key
	 * key myObj, this expression can bind to A's value "myObj.label + myObj.letter".
	 *
	 * @param key - a string literal used to identify object in the expression.
	 * @param val - the actual object or value represented by the key.
	 */
	public void putParameter(String key, Object val) {
		this.getScriptEngine().putVariable(key, val);
	}

	// use this method to validate exp and engine.
	private void validate(String exp) {
		if(StringUtils.isEmpty(exp)) {
			throw new IllegalArgumentException("Unable execute evaluator, missing expression argument.");
		}
	}
}
